/* 
 * Copyright 2010 Ziya Guven Koylu, Ozge Koylu
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package com.koylu.caffein.converter;

import java.math.BigDecimal;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Types;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.beanutils.MethodUtils;

public abstract class Converter {

	public static Map<String, Object> nullValues = null;
	public static Map<Integer,Class<?>> typeMap;
	static {
		nullValues = new HashMap<String, Object>();
		nullValues.put("byte", Byte.valueOf("0"));
		nullValues.put("short", Short.valueOf("0"));
		nullValues.put("int", 0);
		nullValues.put("long", 0L);
		nullValues.put("float", 0.0f);
		nullValues.put("double", 0.0d);
		nullValues.put("char", '\u0000');
		nullValues.put("boolean", false);
	
		typeMap = new HashMap<Integer, Class<?>>();
		typeMap.put(Types.CHAR, String.class);
		typeMap.put(Types.VARCHAR, String.class);
		typeMap.put(Types.LONGVARCHAR, String.class);
		typeMap.put(Types.NUMERIC, BigDecimal.class);
		typeMap.put(Types.DECIMAL, BigDecimal.class);
		typeMap.put(Types.SMALLINT, short.class);
		typeMap.put(Types.INTEGER, int.class);
		typeMap.put(Types.BIGINT, long.class);
		typeMap.put(Types.REAL, float.class);
		typeMap.put(Types.FLOAT, double.class);
		typeMap.put(Types.DOUBLE, double.class);
		typeMap.put(Types.BIT, boolean.class);
		typeMap.put(Types.TINYINT, int.class);
		typeMap.put(Types.BINARY, byte[].class);
		typeMap.put(Types.VARBINARY, byte[].class);
		typeMap.put(Types.LONGVARBINARY, byte[].class);
		typeMap.put(Types.DATE, java.sql.Date.class);
		typeMap.put(Types.TIME, java.sql.Time.class);
		typeMap.put(Types.TIMESTAMP, java.sql.Timestamp.class);
	}
	
	public abstract void setJdbcType(int jdbcType);
	public abstract int getJdbcType();
	public abstract Class<?> getFieldType();
	public abstract void setFieldType(Class<?> fieldType);
	public abstract Object columnToField(ResultSet resultSet, String column) throws Exception;
	public abstract Object columnToField(ResultSet resultSet, int index) throws Exception;
	public abstract Object columnToField(CallableStatement callableStatement, int index) throws Exception;
	
	public void fieldToColumn(PreparedStatement preparedStatement, Object value, boolean columnNullable, int index) throws Exception {
		if (columnNullable && isNull(value)) {
			preparedStatement.setNull(index, getJdbcType());
		} else {
			preparedStatement.setObject(index, value, getJdbcType());
		}
	}
	
	public Class<?> getJavaType(){
		return typeMap.get(getJdbcType());
	}

	public boolean isNull(Object value) throws Exception {
		if (value == null) {
			return true;
		} else if (getFieldType().isPrimitive()) {
			return Boolean.valueOf(MethodUtils.invokeMethod(nullValues.get(getFieldType().getName()), "equals", value).toString());
		}
		return false;
	}
	
	public String objectToString(Object value) throws Exception {
		return value.toString();
	}
}